package com.hsu.hsurpc.spi;

import cn.hutool.core.io.resource.ResourceUtil;
import com.hsu.hsurpc.serializer.Serializer;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * SPI 加载器，用于加载和管理用户自定义的序列化器
 * @Author Hsu琛君珩
 * @Date 2024-09-19 17:51
 * @Description
 * @Version: v1.0.0
 */
@Slf4j
public class SpiLoader {

    /**
     * 存储已加载的接口实现类：接口名 => (key => 实现类)
     */
    private static Map<String, Map<String, Class<?>>> loaderMap = new ConcurrentHashMap<>();

    /**
     * 实例缓存，避免重复 new，使用单例模式：类路径 => 实例
     */
    private static Map<String, Object> instanceCache = new ConcurrentHashMap<>();

    /**
     * 系统 SPI 目录
     */
    private static final String RPC_SYSTEM_SPI_DIR = "META-INF/rpc/system/";

    /**
     * 用户自定义 SPI 目录
     */
    private static final String RPC_CUSTOM_SPI_DIR = "META-INF/rpc/custom/";

    /**
     * 扫描路径
     */
    private static final String[] SCAN_DIRS = new String[]{RPC_SYSTEM_SPI_DIR, RPC_CUSTOM_SPI_DIR};

    /**
     * 动态加载的接口列表
     */
    private static final List<Class<?>> LOAD_CLASS_LIST = List.of(Serializer.class);

    /**
     * 获取某个接口的实例
     *
     * @param tClass 接口类
     * @param key    实现类对应的键
     * @param <T>    泛型
     * @return 接口的实现类实例
     */
    public static <T> T getInstance(Class<?> tClass, String key) {
        String tClassName = tClass.getName();

        // 懒加载外层 map（接口级别）
        Map<String, Class<?>> keyClassMap = loaderMap.computeIfAbsent(tClassName, name -> new ConcurrentHashMap<>());

        // 懒加载内层 map（实现类级别）
        Class<?> implClass = keyClassMap.computeIfAbsent(key, k -> load(tClass, key));

        // 获取实现类实例（单例缓存）
        String implClassName = implClass.getName();
        return (T) instanceCache.computeIfAbsent(implClassName, className -> createInstance(implClass));
    }

    /**
     * 加载指定的实现类
     *
     * @param loadClass 要加载的接口
     * @param key 实现类的名称
     * @return 返回实现类的 Class 对象
     */
    private static Class<?> load(Class<?> loadClass, String key) {
        log.info("懒加载类型为 {}，标识为 {} 的 SPI 实现", loadClass.getName(), key);

        // 尝试加载 SPI 资源
        for (String scanDir : SCAN_DIRS) {
            URL resource = ResourceUtil.getResource(scanDir + loadClass.getName());
            if (resource != null) {
                try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resource.openStream()))) {
                    String line;
                    while ((line = bufferedReader.readLine()) != null) {
                        String[] strArray = line.split("=");
                        if (strArray.length == 2 && strArray[0].equals(key)) {
                            return Class.forName(strArray[1]);
                        }
                    }
                } catch (Exception e) {
                    log.error("SPI 加载资源失败", e);
                }
            }
        }

        throw new RuntimeException(String.format("没有找到 %s 的 SPI 实现类", key));
    }

    /**
     * 通过反射创建实例
     *
     * @param implClass 实现类
     * @return 实例对象
     */
    private static Object createInstance(Class<?> implClass) {
        try {
            return implClass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(String.format("实例化 %s 失败", implClass.getName()), e);
        }
    }
}
